package com.zj.weather.bgviews;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;

import java.util.ArrayList;
import java.util.List;

import com.zj.weather.base.BaseHolder;
import com.zj.weather.base.BaseWeatherBgView;
import com.zj.weather.base.HoldersInfo;
import com.zj.weather.utils.DebugUtils;
import com.zj.weather.utils.DisplayUtils;

import static com.zj.weather.utils.RandomUtils.getRandMax;
import static com.zj.weather.utils.RandomUtils.getRandom;

/**
 * 晴天/日
 */
public class SunnyDrawer extends BaseWeatherBgView {
    private static boolean isNeedDrawSunshine, isNeedRefreshSunshineHolder;
    private static float curFraction = 0;
    private static final float curSunnySize = 60.0f;
    private static final int SUNNY_COUNT = 10;
    private static final float centerOfWidth = 0.87f;
    private static final float centerOfHeight = 0.09f;

    private static final float speed[] = {0.009f, 0.013f};

    public SunnyDrawer(Context context) {
        super(context);
    }

    @Override
    protected List<HoldersInfo> getHolders() {
        List<HoldersInfo> infos = new ArrayList<>();
        infos.add(new HoldersInfo(SUNNY_COUNT) {
            @Override
            public SunnyHolder getHolderType(int position) {
                return new SunnyHolder(width, height, curSunnySize, position);
            }
        });
        infos.add(new HoldersInfo(1) {
            @Override
            public SunShineHolder getHolderType(int position) {
                return new SunShineHolder(width, height, position);
            }
        });
        return infos;
    }

    public static class SunShineHolder extends BaseHolder {
        private static final float sunshineInterval_max = 380f, sunshineInterval_min = 270f;
        private static final float countMax = 15f;
        //光晕移动角度偏离补正
        private static final float angleAdditions = -5f;
        //光晕移动最大角度
        private static final float angle = 15f;
        //位移距离，随着弧度变化，光晕会有y轴的偏差
        private static final float angleOffsetY = 30f;
        //光晕会随机出现的透明值下限
        private static final float minAlpha = 0.65f;
        //光晕每级成长因子，最后加起来不会高于9.9f，高于position * ？== 1.0f -minAlpha 后的值都将按照9.9f渲染；
        private static final float alphaAdditions = 0.02f;
        private List<SunShineInfo> sunShines;
        private final float center_w;
        private final float center_h;
        private boolean postToDraw = false;
        private Paint drawablePaint;

        public SunShineHolder(float canvasWidth, float canvasHeight, int position) {
            super(canvasWidth, canvasHeight, position);
            center_w = canvasWidth * centerOfWidth;
            center_h = canvasHeight * centerOfHeight;
        }

        @Override
        public void initData(Context context) {
            postToDraw = false;
            if (sunShines == null) sunShines = new ArrayList<>();
            else sunShines.clear();
            if (drawablePaint == null) {
                drawablePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            }
            float interval = DisplayUtils.dip2px(context, getRandom(sunshineInterval_min, sunshineInterval_max));
            int count = (int) (countMax * (interval / DisplayUtils.dip2px(context, sunshineInterval_max)));
            for (int i = 0; i < count; i++) {
                SunshineRadialsInfo sunshineRadialsInfo = getSunshineStep();
                float drawableSize = DisplayUtils.dip2px(context, getRandMax((sunshineRadialsInfo.maxSize - sunshineRadialsInfo.minSize), 1.5f) + sunshineRadialsInfo.minSize);
                float offsetY = getRandom(0.0f, 1.0f) * angleOffsetY * 2f - angleOffsetY;
                float sunShineOffset = i == 0 ? 0 : DisplayUtils.dip2px(context, offsetY);
                float drawableInterval;
                final int maxSize = count - 1;
                if (i == 0)
                    drawableInterval = DisplayUtils.dip2px(context, curSunnySize);
                else if (i == maxSize)
                    drawableInterval = interval + DisplayUtils.dip2px(context, curSunnySize);
                else
                    drawableInterval = getRandom(0.3f, 0.8f) * interval;
                float drawableAlpha = getRandom(Math.min(minAlpha + (i * alphaAdditions), 9.9f), 1.0f);
                sunShines.add(new SunShineInfo(i, drawableInterval, drawableSize, sunShineOffset, drawableAlpha, sunshineRadialsInfo));
            }
            postToDraw = true;
        }

        @Override
        public void updateFrame(Context context, Canvas canvas, float changedAlpha) {
            if (isNeedDrawSunshine) {
                if (isNeedRefreshSunshineHolder) {
                    isNeedRefreshSunshineHolder = false;
                    initData(context);
                }
                if (postToDraw) {
                    float curAngle = angle - curFraction * angle + angleAdditions;
                    if (curAngle != 0 && curAngle != angle) {
                        for (SunShineInfo info : sunShines) {
                            float cur_interval = info.interval + curFraction * info.sunshineOffset;
                            PointF radiusCenter = new PointF();
                            radiusCenter.x = Math.abs((float) (Math.sin(curAngle * Math.PI / 180d) * cur_interval));
                            radiusCenter.y = Math.abs((float) (Math.cos(curAngle * Math.PI / 180d) * cur_interval));
                            float drawableSize = info.drawableSize + (curFraction * (info.position / countMax) * info.drawableSize);
                            initPaint(radiusCenter, info, changedAlpha);
                            int drawableLeft = (int) (center_w - radiusCenter.x);
                            int drawableTop = (int) (center_h + radiusCenter.y);
                            canvas.drawCircle(drawableLeft, drawableTop, drawableSize / 2f, drawablePaint);
                        }
                    }
                }
            }
        }

        private void initPaint(PointF radiusCenter, SunShineInfo info, float changeAlpha) {
            float alphaFraction;
            float minSet = 0.36f;
            float maxSet = 0.85f;
            if (curFraction <= minSet) {
                alphaFraction = curFraction / minSet;
            } else if (curFraction >= maxSet) {
                alphaFraction = 1.0f - (curFraction - maxSet) / (1.0f - maxSet);
            } else {
                alphaFraction = 1.0f;
            }
            RadialGradient radialGradient = new RadialGradient(center_w - radiusCenter.x, center_h + radiusCenter.y, info.drawableSize / 2f, info.radialsInfo.colors, info.radialsInfo.steps, Shader.TileMode.CLAMP);
            drawablePaint.reset();
            drawablePaint.setAlpha((int) (alphaFraction * info.sunshineAlpha * 255f * changeAlpha));
            drawablePaint.setShader(radialGradient);
        }

        private SunshineRadialsInfo getSunshineStep() {
            int code = Math.round(getRandom(0.0f, 0.4f) * 10f);
            float[] steps;
            int[] colors;
            float minSize;
            float maxSize;
            switch (code) {

                case 0:
                    steps = new float[]{0.77f, 0.92f, 0.95f, 0.99f, 1.0f};
                    colors = new int[]{0x07ffffff, 0x30f9a63c, 0x30a9fda0, 0x29638bf0, 0x00ffffff};
                    minSize = 48;
                    maxSize = 82;
                    break;
                case 1:
                    steps = new float[]{0.18f, 0.21f, 0.66f, 0.92f, 1.0f};
                    colors = new int[]{0x00ffffff, 0x07ffffff, 0x09ffffff, 0x70ffffff, 0x00ffffff};
                    minSize = 25;
                    maxSize = 52;
                    break;
                case 2:
                    steps = new float[]{0.88f, 1.0f};
                    colors = new int[]{0x30a3f6c2, 0x00ffffff};
                    minSize = 5;
                    maxSize = 22;
                    break;
                case 3:
                    steps = new float[]{0.38f, 0.51f, 0.66f, 0.92f, 1.0f};
                    colors = new int[]{0x30fafac8, 0x07f3d700, 0x09ffffff, 0x70ffffff, 0x00ffffff};
                    minSize = 3;
                    maxSize = 18;
                    break;
                default:
                    steps = new float[]{0.48f, 0.92f, 1.0f};
                    colors = new int[]{0x70fff0c7, 0x70ffffff, 0x00ffffff};
                    minSize = 8;
                    maxSize = 32;
                    break;
            }
            return new SunshineRadialsInfo(steps, colors, maxSize, minSize);
        }
    }

    public static class SunshineRadialsInfo {
        private final float[] steps;
        private final int[] colors;
        private final float maxSize, minSize;

        public SunshineRadialsInfo(float[] steps, int[] colors, float maxSize, float minSize) {
            this.steps = steps;
            this.colors = colors;
            this.maxSize = maxSize;
            this.minSize = minSize;
        }
    }

    public static class SunShineInfo {
        //光晕大小
        private final float drawableSize;
        //这个Holder在当前光晕带中的位置，由上往下，0开始；
        private final int position;
        //光晕的初始距离
        private final float interval;
        //光晕在旋转途中会有一个Y轴的变化，这个值表示变化的最大范围，正数往下，负数往上，单位为DP；
        private final float sunshineOffset;
        //光晕的透明值，越往下的基数上会高于往上的，且增加随机数
        private final float sunshineAlpha;
        //光晕样式，暂设计了5种;
        private SunshineRadialsInfo radialsInfo;

        public SunShineInfo(int position, float interval, float drawableSize, float sunShineOffset, float sunshineAlpha, SunshineRadialsInfo radialsInfo) {
            this.position = position;
            this.interval = interval;
            this.drawableSize = drawableSize;
            this.sunshineOffset = sunShineOffset;
            this.sunshineAlpha = sunshineAlpha;
            this.radialsInfo = radialsInfo;
        }
    }

    public static class SunnyHolder extends BaseHolder {
        private float curSize, center_w, center_h;
        private float curAlpha;// [0,1]
        //光亮到何种程度通知绘制光晕
        private static final float maxLight = 0.75f;
        private boolean alphaIsGrowing = true, isRefreshParams = false;

        public SunnyHolder(int width, int height, float curSize, int position) {
            super(width, height, position + 1);
        }

        @Override
        public void initData(Context context) {
            this.curSize = DisplayUtils.dip2px(context, curSunnySize);
            center_w = canvasWidth * centerOfWidth;
            center_h = canvasHeight * centerOfHeight;
            curAlpha = getAlpha(position)[0];
        }

        @Override
        public void updateFrame(Context context, Canvas canvas, float changedAlpha) {
            float[] alphas = getAlpha(position);
            float maxAlpha = alphas[0], minAlpha = alphas[1];
            final float delta = getRandom(speed[0], speed[1]) * (maxAlpha - minAlpha);
            if (alphaIsGrowing) {
                if (!isRefreshParams) {
                    isRefreshParams = true;
                    refreshParams();
                }
                curAlpha += delta;
                if (curAlpha > maxAlpha) {
                    curAlpha = maxAlpha;
                    alphaIsGrowing = false;
                }
            } else {
                if (isRefreshParams) isRefreshParams = false;
                curAlpha -= delta;
                if (curAlpha < minAlpha) {
                    curAlpha = minAlpha;
                    alphaIsGrowing = true;
                }
            }

            if (this.position == SUNNY_COUNT - 1) {
                float triggerAlpha = maxAlpha - (maxAlpha * maxLight);
                float mMaxAlpha = triggerAlpha * 2f;
                float mMinAlpha = (maxAlpha * maxLight);
                isNeedDrawSunshine = curAlpha >= mMinAlpha;
                if (isNeedDrawSunshine) {
                    float curSunshineAlpha = alphaIsGrowing ? curAlpha - mMinAlpha : triggerAlpha + (maxAlpha - curAlpha);
                    setSunshineFraction(mMaxAlpha, curSunshineAlpha);
                }
            }
            Drawable drawable = getDrawable(position);
            drawable.setAlpha((int) (255 * curAlpha * changedAlpha));
            drawable.draw(canvas);
            DebugUtils.e("" + curAlpha);
        }

        private void setSunshineFraction(float maxAlpha, float curAlpha) {
            curFraction = curAlpha / maxAlpha;
        }

        private Rect getBounds(int position) {
            float radius = (curSize * position) / 2f;
            int left = (int) (center_w - radius);
            int top = (int) (center_h - radius);
            int right = (int) (center_w + radius);
            int bottom = (int) (center_h + radius);
           return new Rect(left, top, right, bottom);
        }

        private void refreshParams() {
            if (alphaIsGrowing) {
                if (!isNeedRefreshSunshineHolder && this.position == SUNNY_COUNT - 1)
                    isNeedRefreshSunshineHolder = true;
            }
        }

        private GradientDrawable getDrawable(int position) {
            GradientDrawable drawable;
            switch (position) {
                case 1://圆心
                    drawable = new GradientDrawable(GradientDrawable.Orientation.TR_BL, new int[]{0xccf0fad9, 0xccf4fae7});
                    break;
                default://周围日冕
                    drawable = new GradientDrawable(GradientDrawable.Orientation.BL_TR, new int[]{0x70ffffff, 0x9bffffff, 0xaaffffff, 0x50000000, 0x09000000, 0x00000000});
                    break;
            }
            drawable.setBounds(getBounds(position));
            drawable.setShape(GradientDrawable.OVAL);
            drawable.setGradientRadius((curSize * position) / 2f);
            drawable.setGradientType(GradientDrawable.RADIAL_GRADIENT);
            return drawable;
        }

        private float[] getAlpha(int position) {
            float[] alpha = new float[2];
            switch (position) {
                case 1://圆心透明度变换较小，明暗不明显
                    alpha[0] = 0.96f;
                    alpha[1] = 0.85f;
                    break;
                default://周围日冕透明度变换稍大，越往后越淡；
                    alpha[0] = 0.85f / position;
                    alpha[1] = 0.23f / position;
                    break;
            }
            return alpha;
        }
    }
}